package ru.ksu.ktk.diploma.sdi.util;

import java.io.IOException;
import java.io.StringWriter;
import java.io.Writer;
import java.util.ArrayList;
import java.util.List;

import javax.xml.transform.*;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;

import org.apache.xerces.parsers.DOMParser;
import org.w3c.dom.*;
import org.xml.sax.SAXException;

import com.sun.org.apache.xerces.internal.dom.DOMImplementationImpl;

import ru.ksu.ktk.diploma.sdi.mapping.Correspondence;
import ru.ksu.ktk.diploma.sdi.mapping.MappingElement;
import ru.ksu.ktk.diploma.sdi.mapping.Triple;

public class DOMUtil {
    private static final String ROOT_EL = "correspondences";
    private static final String RANGE_EL = "range";
    private static final String PROPERTY_EL = "property";
    private static final String DOMAIN_URI_EL = "domain";
    private static final String TRIPLE_EL = "triple";
    private static final String WEIGHT_EL = "weight";
    private static final String MAPPING_ELEMENT_EL = "mapping_element";
    private static final String URI_EL = "uri";
    private static final String CORRESPONDENCE_EL = "correspondence";

    private DOMUtil() {
    }

    public static Document convertToDOM(List<Correspondence> correspondences) {
        DOMImplementation impl = DOMImplementationImpl.getDOMImplementation();
        Document document = impl.createDocument(null, null, null);
        Element rootElement = document.createElement(ROOT_EL);
        document.appendChild(rootElement);
        for (Correspondence corr : correspondences){
            Element corrElement = document.createElement(CORRESPONDENCE_EL);
            rootElement.appendChild(corrElement);
            corrElement.setAttribute(URI_EL, corr.getSourceClassUri());
            List<MappingElement> mappingElements = corr.getMappingElements();
            for (MappingElement mapEl : mappingElements){
                Element mapElement = document.createElement(MAPPING_ELEMENT_EL);
                mapElement.setAttribute(URI_EL, mapEl.getSrcClassURI());
                mapElement.setAttribute(WEIGHT_EL, String.valueOf(mapEl.getWeight()));
                Element tripleElement = document.createElement(TRIPLE_EL);
                Triple triple = mapEl.getTargetTriple();
                tripleElement.setAttribute(DOMAIN_URI_EL, triple.getDomainUri());
                tripleElement.setAttribute(PROPERTY_EL, triple.getPathUri());
                tripleElement.setAttribute(RANGE_EL, triple.getRangeUri());
                mapElement.appendChild(tripleElement);
                corrElement.appendChild(mapElement);

            }
        }

        return document;
    }

    public static List<Correspondence> convertFromDOM(Document document) {
        List<Correspondence> corrs = new ArrayList<Correspondence>();
        Element rootEl = document.getDocumentElement();
        NodeList corrNodes = rootEl.getChildNodes();
        for (int i = 0; i < corrNodes.getLength(); i++){

            Node item = corrNodes.item(i);
            if (!item.getNodeName().equals(CORRESPONDENCE_EL)) continue;

            Element corrItem = (Element) item;
            String uri = corrItem.getAttribute(URI_EL);
            List<MappingElement> mappingElements = new ArrayList<MappingElement>();
            NodeList mapElNodes = corrItem.getChildNodes();
            for (int j = 0; j < mapElNodes.getLength(); j++){
                Node mapItem = mapElNodes.item(j);
                if (!mapItem.getNodeName().equals(MAPPING_ELEMENT_EL)) continue;
                Element mapElItem = (Element) mapItem;
                String mapUri = mapElItem.getAttribute(URI_EL);
                int weight = Integer.parseInt(mapElItem.getAttribute(WEIGHT_EL));
                NodeList mapElChildNodes = mapElItem.getChildNodes();
                Triple triple = null;
                for (int k = 0; k < mapElChildNodes.getLength(); k++){
                    Node mapElChild = mapElChildNodes.item(k);
                    if (mapElChild.getNodeName().equals(TRIPLE_EL)){
                        Element tripleEl = (Element) mapElChild;
                        String domainUri = tripleEl.getAttribute(DOMAIN_URI_EL);
                        String propertyUri = tripleEl.getAttribute(PROPERTY_EL);
                        String rangeUri = tripleEl.getAttribute(RANGE_EL);
                        triple = new Triple(domainUri, propertyUri, rangeUri);
                        break;
                    }
                }

                MappingElement mappingElement = new MappingElement.Builder(mapUri, triple).weight(weight).build();
                mappingElements.add(mappingElement);
            }
            Correspondence corr = new Correspondence(uri);
            corr.setMappingElements(mappingElements);
            corrs.add(corr);
        }
        return corrs;
    }

    public static Document getDocument(String text) throws SAXException, IOException {
        DOMParser parser = new DOMParser();
        parser.parse(text);
        return parser.getDocument();
    }

    public static String convertToString(Document document) throws TransformerException {
        TransformerFactory xformFactory = TransformerFactory.newInstance();
        Transformer idTransform = xformFactory.newTransformer();
        Source input = new DOMSource(document);
        Writer writer = new StringWriter();

        Result output = new StreamResult(writer);
        idTransform.transform(input, output);
        return writer.toString();
    }

}
